package com.awesumgames.awesum;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import com.awesumgames.awesum.AwesumGlobal.Awesum;
import com.awesumgames.awesum.controls.Control.Ouya;

import android.opengl.GLES20;
import android.opengl.Matrix;
import android.os.SystemClock;

public final class Models {
	final static int StrideBytes = 7 * Game.FloatSize;
	final static int PositionDataSize = 3;
	final static int PositionOffset = 0;
	final static int ColorDataSize = 4;
	final static int ColorOffset = 3;
	private static Models _this = new Models();
	
	public static final Colored Colored()			{return _this.new Colored();}
	public static final Textured Textured(int tex)	{return _this.new Textured(tex);}

	public abstract class Model {
		public FloatBuffer vertices = null;
		public FloatBuffer normals = null;
		public ShortBuffer indices = null;
		final static int BytesPerFloat = 4;
		int StrideBytes = 0;
		int PositionDataSize = 0;
		int PositionOffset = 0;
		int NormalDataSize = 0;
		int NormalOffset = 0;
		int ColorDataSize = 0;
		int ColorOffset = 0;
		int TextureDataSize = 0;
		int TextureOffset = 0;
		Vector4 color = new Vector4();
		int verticesVbo = -1;
		int normalsVbo = -1;
		int indicesVbo = -1;
		
		int drawMode = GLES20.GL_TRIANGLES;
		void load() {}
		void draw() {}
	}
	public class Colored extends Model {
		private float lightAng = 0f;
		private Vector3 lightPos = new Vector3();
		Colored() {
			StrideBytes = 3 * Game.FloatSize;
			PositionDataSize = 3;
			PositionOffset = 0;
			NormalDataSize = 3;
			NormalOffset = 0;
			ColorDataSize = 4;
			ColorOffset = 0;
			color.set(1f, .8f, .8f, 1f);
		}
		void load() {
			if (verticesVbo == -1) {
				final int buffers[] = new int[3];
				GLES20.glGenBuffers(3, buffers, 0);
				GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[0]);
				GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertices.capacity() * Game.FloatSize, vertices, GLES20.GL_STATIC_DRAW);

				GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buffers[1]);
				GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, normals.capacity() * Game.FloatSize, normals, GLES20.GL_STATIC_DRAW);
				
				GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, buffers[2]);
				GLES20.glBufferData(GLES20.GL_ELEMENT_ARRAY_BUFFER, indices.capacity() * Game.ShortSize, indices, GLES20.GL_STATIC_DRAW);
				//GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
				verticesVbo = buffers[0];
				normalsVbo = buffers[1];
				indicesVbo = buffers[2];
			}
		}
		void draw() {
			Game.Shaders.use("color");
			GLES20.glUniformMatrix4fv(Game.Shaders.current.mvp, 1, false, Game.getMVP(), 0);
			GLES20.glUniformMatrix4fv(Game.Shaders.current.mv, 1, false, Game.mvMatrix, 0);
			GLES20.glUniform4f(Game.Shaders.current.color, color.x, color.y, color.z, color.a);
			
			float[] lightModel = new float[] {0f, 0f, 0f, 1f};
			float[] lightWorld = new float[4];
			float[] lightEye = new float[4];
			float[] lightModelMatrix = new float[16];
			Matrix.setIdentityM(lightModelMatrix, 0);
			//Matrix.translateM(lightModelMatrix, 0, 4.5f, 3f, -4f);
			//Matrix.rotateM(lightModelMatrix, 0, 90f, 0f, 0f, 1f);
			//Matrix.translateM(lightModelMatrix, 0, 0f, 0f, 0f);
			
			if (Ouya.stickRMag[0] > 0f) {
				lightPos.x += Ouya.stickRX[0] * .1;
				lightPos.z += Ouya.stickRY[0] * .1;
			}
			if (Ouya.bU[0])
				lightPos.y -= .01f;
			if (Ouya.bY[0])
				lightPos.y += .01f;

			//////////////////Awesum.debug("light pos = " + lightPos.toString() + " ang = " + lightAng);
			/*lightAng += 1f;
			lightAng %= 360f;
			Matrix.translateM(lightModelMatrix, 0, lightPos.x, lightPos.y, lightPos.z);
			Matrix.rotateM(lightModelMatrix, 0, lightAng, 0f, 1f, 0f);
			Matrix.translateM(lightModelMatrix, 0, 0f, 0f, 2f);
			*/
			long time = SystemClock.uptimeMillis() % 10000L;        
	        float angleInDegrees = (360.0f / 10000.0f) * ((int) time);  
	        Matrix.translateM(lightModelMatrix, 0, 0.0f, 0.0f, -5.0f);      
	        Matrix.rotateM(lightModelMatrix, 0, angleInDegrees, 0.0f, 1.0f, 0.0f);
	        Matrix.translateM(lightModelMatrix, 0, 0.0f, 0.0f, 2.0f);
			
			Matrix.multiplyMV(lightWorld, 0, lightModelMatrix, 0, lightModel, 0);
			Matrix.multiplyMV(lightEye, 0, Game.viewMatrix, 0, lightWorld, 0);
			GLES20.glUniform3f(Game.Shaders.current.light, lightEye[0], lightEye[1], lightEye[2]);
			
	        //vertices/color coords
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, verticesVbo);
			GLES20.glVertexAttribPointer(Game.Shaders.current.pos, PositionDataSize, GLES20.GL_FLOAT, false, StrideBytes, PositionOffset * Game.FloatSize);
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, normalsVbo);
			GLES20.glVertexAttribPointer(Game.Shaders.current.normal, NormalDataSize, GLES20.GL_FLOAT, false, StrideBytes, NormalOffset * Game.FloatSize);
			
			GLES20.glDrawElements(drawMode, indices.capacity(), GLES20.GL_UNSIGNED_SHORT, indices);

			//unbind
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
		}
	}
	public class Textured extends Model {
		public int texture = -1;
		Textured(int texHandle) {
			texture = texHandle;
			StrideBytes = 5 * BytesPerFloat;
			PositionDataSize = 3;
			PositionOffset = 0;
			TextureDataSize = 2;
			TextureOffset = 3;
		}
		void load() {
			
		}
		void draw() {
			GLES20.glUniformMatrix4fv(Game.Shaders.current.mvp, 1, false, Game.getMVP(), 0);

			GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
	        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
	        GLES20.glUniform1i(Game.Shaders.current.texture, 0);
			
	        //vertices/color coords
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, verticesVbo);
			GLES20.glVertexAttribPointer(Game.Shaders.current.pos, PositionDataSize, GLES20.GL_FLOAT, false, StrideBytes, PositionOffset * Game.FloatSize);
			GLES20.glVertexAttribPointer(Game.Shaders.current.texCoord, TextureDataSize, GLES20.GL_FLOAT, false, StrideBytes, TextureOffset * Game.FloatSize);
			
			GLES20.glDrawElements(drawMode, indices.capacity(), GLES20.GL_UNSIGNED_SHORT, indices);

			//unbind
			GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
			GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
		}
	}
	public class SpriteStatic extends Model {
		public int StrideBytes = 5 * BytesPerFloat;
		public int PositionDataSize = 3;
		public int PositionOffset = 0;
		public int TextureDataSize = 2;
		public int TextureOffset = 3;
		public boolean initialized = false;
		
		void draw(int texture) {
			GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
	        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
	        GLES20.glUniform1i(Game.Shaders.current.texture, 0);
			
			vertices.position(PositionOffset);
			GLES20.glVertexAttribPointer(Game.Shaders.current.pos, PositionDataSize, GLES20.GL_FLOAT, false, StrideBytes, vertices);
			
			vertices.position(TextureOffset);
			GLES20.glVertexAttribPointer(Game.Shaders.current.texCoord, TextureDataSize, GLES20.GL_FLOAT, false, StrideBytes, vertices);
			
			GLES20.glUniformMatrix4fv(Game.Shaders.current.mvp, 1, false, Game.getMVP(), 0);
			if (indices == null)
				GLES20.glDrawArrays(drawMode, 0, 3);
			else
				GLES20.glDrawElements(drawMode, indices.capacity(), GLES20.GL_UNSIGNED_SHORT, indices);
		}
	}
	public class SpriteDynamic extends Model {
		public int texture = -1;
		SpriteDynamic(int texHandle) {
			texture = texHandle;
			StrideBytes = 5 * BytesPerFloat;
			PositionDataSize = 3;
			PositionOffset = 0;
			TextureDataSize = 2;
			TextureOffset = 3;
		}
		void draw() {
			GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
	        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
	        GLES20.glUniform1i(Game.Shaders.current.texture, 0);
			
			vertices.position(PositionOffset);
			GLES20.glVertexAttribPointer(Game.Shaders.current.pos, PositionDataSize, GLES20.GL_FLOAT, false, StrideBytes, vertices);
			
			vertices.position(TextureOffset);
			GLES20.glVertexAttribPointer(Game.Shaders.current.texCoord, TextureDataSize, GLES20.GL_FLOAT, false, StrideBytes, vertices);
			
			GLES20.glUniformMatrix4fv(Game.Shaders.current.mvp, 1, false, Game.getMVP(), 0);
			if (indices == null)
				GLES20.glDrawArrays(drawMode, 0, 3);
			else
				GLES20.glDrawElements(drawMode, indices.capacity(), GLES20.GL_UNSIGNED_SHORT, indices);
		}
	}
}
